using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using Athena;
using Athena.Tools;
using Athena.Objects;
using Athena.Core;

using WeifenLuo.WinFormsUI.Docking;

namespace SequenceViewerPlugin
{
    public partial class SequenceViewGUI : Athena.Tools.IGUITool
    {

        public SequenceViewGUI()
        {
            InitializeComponent();

            SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.UserPaint, true);

            Text = "Sequence View";
            _bRequiresNewToolWindow = true;
            ToolManager.Instance.RegisterTool(Text, this);
        }

        public override void SetupToolStrip(ToolStrip oStrip)
        {
            ToolStripButton oButton = new ToolStripButton();
            oButton.Text = Text;
            oButton.ToolTipText = "View the DNA sequence of the selected part(s)";
            oButton.Click += new EventHandler(ShowHide_Click);
            oStrip.Items.Add(oButton);
        }

        public override void SetupDockPanel(WeifenLuo.WinFormsUI.Docking.DockPanel oPanel)
        {
            this.DockPanel = oPanel;
        }

        public override void ShowHide_Click(object sender, EventArgs e)
        {
            if (IsHidden)
            {
                //DockState = WeifenLuo.WinFormsUI.Docking.DockState.Float;
                Show();                
            }
            else
            {
                Hide();
            }
        }


        void MainForm_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            this.Hide();
            e.Cancel = true;
        }

        /// <summary>
        /// returns the first DNAPathGlyph in the given chain of DNAPartGlyphs. Returns null if any of the 
        /// drawables are not DNAPartGlyphs or if they are all not connected in a single chain
        /// </summary>
        /// <param name="DNAParts"></param>
        /// <returns></returns>
        private List<Part> AllConnectedParts(List<IDrawable> drawables)
        {
            List<IDrawable> DNAParts = new List<IDrawable>();

            if (drawables == null || drawables.Count < 1) return new List<Part>();

            foreach (IDrawable id in drawables)
            {
                if (id.Item is Part)
                {
                    DNAParts.Add(id);
                }
                if (id.Item is Module)
                {
                    Hashtable allparts = ((Module)id.Item).AllParts;
                    foreach (Part p in allparts.Values)
                        if (p.Drawable != null)
                            DNAParts.Add(p.Drawable);
                }
            }

            if (DNAParts == null || DNAParts.Count == 0) return new List<Part>();

            Part leftMost = ((Part)DNAParts[0].Item).LeftMostGlobal;

            List<Part> list = new List<Part>();

            Part rightMost = leftMost.RightMostGlobal;

            Part part = leftMost;

            while (part != rightMost)
            {
                list.Add(part);
                part = part.Right;                
            }

            list.Add(part);

            return list;
        }

        public PointF _Clicked;
        public Part _SelectedPart;
        public int _SelectedStart, _SelectedEnd;
        private bool _drawing = true;
        private float _offset = 0;

        public static Color[] PartColors = new Color[] { Color.Violet, Color.Indigo, Color.Blue, Color.Green, Color.GreenYellow, Color.Orange, Color.Red };

        private static Color GetPartColors(int index)
        {
            int cycle = 4;
            int p = (int)(index % (cycle*3));
            int r = 0, g = 0, b = 0;

            if (p <= cycle) { b = 50; g = p * 255 / cycle; r = 255 - g; }
            else
                if (p <= 2*cycle) { p -= cycle; r = p * 255 / cycle; b = 255 - r; g = 50; }
                else
                    if (p <= 3 * cycle) { p -= 2*cycle; b = p * 255 / cycle; g = 255 - b; r = 50; }

            return Color.FromArgb(255,r,g,b);
        }

        /// <summary>
        /// draws the sequence in different colors for different parts
        /// </summary>
        /// <param name="g"></param>
        /// <param name="partsList">List of partGlyphs</param>
        /// <param name="blocksPerLine">num. blocks in each line</param>
        /// <param name="charsPerBlock">num. chars in each block</param>
        protected float DrawText(Graphics g, List<Part> partsList, int blocksPerLine, int charsPerBlock)
        {
            _drawing = true;
            Font font = SystemFonts.GetFontByName("Courier New");    //fonts etc
            if (font == null) font = SystemFonts.DialogFont;
            Font fontBig = new Font(font, FontStyle.Italic);

            int partIndex = 0, seqIndex = 0, overallIndex = 1;   //index of the part being drawn, index of that part's sequence
            Part part = partsList[partIndex];          //current part being drawn

            float x = 10, y = _offset + 30;   //where to draw
            
            SolidBrush brush = new SolidBrush(GetPartColors(0));
            
            g.DrawString(partsList[partIndex].ID, fontBig, brush, x, y + font.Size + 10);
            if (partsList[partIndex].Drawable is PartGlyph)
            {
                PartGlyph glyph = (PartGlyph)partsList[partIndex].Drawable;                
                Bitmap bitmap = glyph.GetPreview(20, 15);
                g.DrawImage(bitmap, x + (7 * partsList[partIndex].ID.Length), y + font.Size + 10);
            }

            RectangleF rect;

            while (partIndex < partsList.Count)          //for all parts  in the list
            {
                string block1, block2 = "";                       //each block can possibly be a border - halves of two parts
                int lengthLeft = part.Sequence.Length - seqIndex;   //checking for border

                if (lengthLeft < charsPerBlock)     //reached a border? -- the block contains two parts
                {
                    block1 = part.Sequence.Substring(seqIndex);   //get the rest of current part's sequence
                    
                    g.DrawString(block1, font, brush, x, y);   //draw the last chars of the part

                    ++partIndex;

                    if (partIndex < partsList.Count) //if not the last part in the list...
                    {
                        seqIndex = 0;                  //set next part's seq's starting point
                        part = partsList[partIndex];     //move to next part

                        brush = new SolidBrush(GetPartColors(partIndex));   //next color

                        if (part.Sequence.Length >= charsPerBlock - lengthLeft)
                        {
                            block2 = part.Sequence.Substring(seqIndex, charsPerBlock - lengthLeft);
                            seqIndex = charsPerBlock - lengthLeft;
                            g.DrawString(block2, font, brush, x + lengthLeft * font.Size - 1, y);   //draw the first chars of the part
                            g.DrawString(partsList[partIndex].ID, fontBig, brush, x + lengthLeft * font.Size - 1, y + font.Size + 10);  //write the name
                            if (partsList[partIndex].Drawable is PartGlyph)
                            {
                                PartGlyph glyph = (PartGlyph)partsList[partIndex].Drawable;
                                Bitmap bitmap = glyph.GetPreview(20, 15);
                                g.DrawImage(bitmap, x + lengthLeft * font.Size - 1 + (7 * partsList[partIndex].ID.Length), y + font.Size + 10);
                            }
                        }
                        else
                        {
                            block2 = part.Sequence;
                            g.DrawString(block2, font, brush, x + lengthLeft * font.Size - 1, y);   //draw the first chars of the part
                            continue;
                        }
                        
                    }
                }
                else
                {
                    block1 = part.Sequence.Substring(seqIndex, charsPerBlock);
                    block2 = "";
                    g.DrawString(block1, font, brush , x, y);   //draw the chars
                }

                rect = new RectangleF(x, y, charsPerBlock * font.Size, 20); //the rect around the current text

                if (rect.Contains(_Clicked))  //if this region is clicked on
                {
                    _ModifySeqBox.Visible = _ModifySeqBox.Enabled = true;   //draw the textbox
                    _ModifySeqBox.Height = 20; 

                    _SelectedStart = seqIndex;   
                    _SelectedPart = part;

                    if (_Clicked.X < x + lengthLeft * font.Size)
                    {   
                        _ModifySeqBox.Location = new Point((int)x - 1, (int)y - 1);
                        _ModifySeqBox.Width = block1.Length * (int)font.Size - 1;
                        _ModifySeqBox.Text = block1;

                        if (block2.Length > 0)   //border of two parts
                        {
                            _SelectedPart = partsList[partIndex - 1];
                            _SelectedStart = partsList[partIndex - 1].Sequence.Length - block1.Length;
                            _SelectedEnd = partsList[partIndex - 1].Sequence.Length;
                        }
                        else
                        {
                            _SelectedEnd = seqIndex + block1.Length;
                        }
                    }
                    else    //only for the case when a new part is starting from the middle of a block
                    {
                        _ModifySeqBox.Location = new Point((int)(x + lengthLeft * font.Size - 1), (int)y - 1);
                        _ModifySeqBox.Width = block2.Length * (int)font.Size;
                        _ModifySeqBox.Text = block2;
                        _SelectedStart = 0;
                        _SelectedEnd = block2.Length;
                    }
                }

                g.DrawString(overallIndex.ToString(), font, new SolidBrush(Color.Black), x, y - 1.5f * font.Size);
                x += charsPerBlock * 8 + 4;

                if (x >= blocksPerLine * (charsPerBlock * 8 + 10))
                {
                    x = 10;
                    y += 60;
                }

                if (lengthLeft >= charsPerBlock) seqIndex += charsPerBlock;
                overallIndex += charsPerBlock;
            }
            _drawing = false;
            return y;
        }

        private void Draw(Graphics g)
        {
            if (CurrentModel.Selected.Count < 1)
            {
                return;
            }

            List<Part> partsList = AllConnectedParts(CurrentModel.Selected);
            if (partsList == null || partsList.Count < 1)
            {
                return;
            }

            float y = DrawText(g, partsList, (int)(drawPanel.Width / 100), 10);

            if (_offset < -y - 500)
            {
                _offset = -y - 500;
                drawPanel.Refresh();
            }
            if (_offset > 10)
            {
                _offset = 10;
                drawPanel.Refresh();
            }

            _startLabel.Location = new Point(0, drawPanel.Location.Y - 10);
            _endLabel.Location = new Point(0, drawPanel.Size.Height + 100);
        }

        private void drawPanel_Paint(object sender, PaintEventArgs e)
        {
            
            Draw(e.Graphics);            
        }

        void drawPanel_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
        {
            _offset += e.OldValue - e.NewValue;
            
            drawPanel.Refresh();
        }

        private void drawPanel_MouseClick(object sender, MouseEventArgs e)
        {
            _ModifySeqBoxChanged();

            _Clicked = new PointF(e.X, e.Y);
            _ModifySeqBox.Visible = _ModifySeqBox.Enabled = false;
            drawPanel.Refresh();

            if (_SelectedPart != null)
            {
                sequenceTextBox.Text = ValidDNA(_SelectedPart.Sequence);
                sequenceTextBox.Refresh();
            }
        }

        private void _ModifySeqBoxChanged()
        {
            if (_drawing) return;

            try
            {
                if (_SelectedPart != null && !string.IsNullOrEmpty(_ModifySeqBox.Text))
                {
                    //System.Diagnostics.Debug.WriteLine(_SelectedPart.ID);
                    //System.Diagnostics.Debug.WriteLine(_SelectedStart + " " + _SelectedEnd);

                    string seq1 = _SelectedPart.Sequence.Substring(0, _SelectedStart),
                           seq2 = "";

                    if (_SelectedEnd < _SelectedPart.Sequence.Length)
                    {
                        seq2 = _SelectedPart.Sequence.Substring(_SelectedEnd);
                        _SelectedPart.Sequence = ValidDNA(seq1 + _ModifySeqBox.Text + seq2);
                    }
                    else
                    {
                        _SelectedPart.Sequence = ValidDNA(seq1 + _ModifySeqBox.Text);
                    }

                    _ModifySeqBox.Text = "";
                }
            }
            catch (Exception)
            {
            }
        }

        private void _ModifySeqBox_TextChanged(object sender, EventArgs e)
        {
            _ModifySeqBoxChanged();
        }

        private void sequenceTextBox_TextChanged(object sender, EventArgs e)
        {
            if (_SelectedPart != null)
            {
                _SelectedPart.Sequence = ValidDNA(sequenceTextBox.Text);
                drawPanel.Refresh();
            }
            //Clipboard.SetData(DataFormats.Text, sequenceTextBox.Text);
        }

        private void drawPanel_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.P:
                    if (ModifierKeys == Keys.Control)
                    {
                        if (printDialog.ShowDialog() == DialogResult.OK)
                        {
                            printDocument.PrinterSettings = printDialog.PrinterSettings;
                            printDocument.Print();
                        }
                    }
                    break;
                default:
                    break;
            }
        }

        private void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.None;
            e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;

            Draw(e.Graphics);
        }

        private static string ValidDNA(string seq)
        {
            if (seq == null) return null;

            StringBuilder str = new StringBuilder();
            for (int i = 0; i < seq.Length; ++i)
            {
                char n = seq[i];
                if (n != 'A' && n != 'a' &&
                    n != 'C' && n != 'c' &&
                    n != 'G' && n != 'g' &&
                    n != 'T' && n != 't' &&
                    n != 'N' && n != 'n')
                    str.Append('N');
                else
                    str.Append(n);
            }
            return str.ToString();
        }

        /// <summary>
        /// Prevent some nasty keys ...
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (!Focused) return false;

            switch (keyData)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Back:
                case Keys.Tab:
                case Keys.Enter:
                case Keys.Home:
                case Keys.Control:
                    return true;

                default:
                    return base.ProcessCmdKey(ref msg, keyData);
            }
        }


    }
}
